﻿using GalaSoft.MvvmLight.CommandWpf;
using GEPL.InterfaceLibrary;
using GEPL.Projects;
using GEPL.Projects.UserControls;
using GEPL.StandardModulesLibrary.Elements.Common;
using GEPL.StandardModulesLibrary.Elements.Console;
using GEPL.StandardModulesLibrary.Elements.Turtle;
using GEPL.Utils;
using GEPL.Views;
using Microsoft.Win32;
using PropertyChanged;
using System;
using System.Collections.ObjectModel;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace GEPL.ViewModels
{
    /// <summary>
    /// View model for main application window (MainWindow)
    /// </summary>
    [ImplementPropertyChanged]
    public class MainWindowViewModel
    {
        #region Fields

        // we need this to show modal dialogs in the center of application window
        private readonly MainWindow owner;

        // we need this to dynamically add proper output control
        private readonly Grid mainGrid;

        private UserControl outputControl;

        private HelpWindow helpWindow;

        #endregion Fields

        #region Properties

        /// <summary>
        /// List of elements available for user (they can be dragged into workspace)
        /// </summary>
        public ObservableCollection<IElement> ToolboxElementsList { get; private set; }

        /// <summary>
        /// View model for Workspace user control.
        /// </summary>
        public WorkspaceViewModel WorkspaceViewModel { get; set; }

        #endregion Properties

        #region Commands

        public ICommand NewConsoleProjectCommand { get; set; }

        public ICommand NewTurtleProjectCommand { get; set; }

        public ICommand OpenProjectCommand { get; set; }

        public ICommand SaveAsCommand { get; set; }

        public ICommand CloseProjectCommand { get; set; }

        public ICommand ExecuteCommand { get; set; }

        public ICommand ShowCodeCommand { get; set; }

        public ICommand ShowAboutCommand { get; set; }

        public ICommand ExpertModeCommand { get; set; }

        public ICommand SaveOutputCommand { get; set; }

        public ICommand ChangeLanguageCommand { get; set; }

        public ICommand ShowHelpCommand { get; set; }

        #endregion Commands

        #region Languages

        public bool AutoLangChecked { get; set; }

        public bool EnLangChecked { get; set; }

        public bool PlLangChecked { get; set; }

        #endregion Languages

        /// <summary>
        /// Creates an instance of class
        /// </summary>
        /// <param name="owner">Owner window</param>
        public MainWindowViewModel(MainWindow owner)
        {
            WorkspaceViewModel = new WorkspaceViewModel();
            this.owner = owner;
            mainGrid = owner.MainGrid;
            ToolboxElementsList = new ObservableCollection<IElement>();
            InitCommands();
            InitLanguage();
        }

        #region Initialization methods

        /// <summary>
        /// Initializes relay commands
        /// </summary>
        private void InitCommands()
        {
            NewConsoleProjectCommand = new RelayCommand(CreateConsoleProject, () => WorkspaceViewModel.Project == null);
            NewTurtleProjectCommand = new RelayCommand(CreateTurtleProject, () => WorkspaceViewModel.Project == null);
            OpenProjectCommand = new RelayCommand(OpenProject, () => WorkspaceViewModel.Project == null);
            SaveAsCommand = new RelayCommand(SaveAs, () => WorkspaceViewModel.Project != null);
            CloseProjectCommand = new RelayCommand(CloseProject, () => WorkspaceViewModel.Project != null && !WorkspaceViewModel.Project.IsExecuted);
            ExecuteCommand = new RelayCommand(Execute, () => WorkspaceViewModel.Project != null && !WorkspaceViewModel.Project.IsExecuted);
            ShowCodeCommand = new RelayCommand(ShowCode, () => WorkspaceViewModel.Project != null);
            ShowAboutCommand = new RelayCommand(ShowAbout);
            ExpertModeCommand = new RelayCommand(ExpertModeUpdate);
            SaveOutputCommand = new RelayCommand(SaveOutput, () => WorkspaceViewModel.Project != null && !WorkspaceViewModel.Project.IsExecuted);
            ChangeLanguageCommand = new RelayCommand(ChangeLanguage);
            ShowHelpCommand = new RelayCommand(ShowHelp);
        }

        private void InitLanguage()
        {
            switch (Properties.Settings.Default.Settings_Language)
            {
                case "auto":
                    AutoLangChecked = true;
                    break;

                case "pl-PL":
                    PlLangChecked = true;
                    break;

                case "en":
                    EnLangChecked = true;
                    break;

                default:
                    AutoLangChecked = true;
                    Properties.Settings.Default.Settings_Language = "auto";
                    break;
            }
        }

        private void InitConsoleOutput()
        {
            outputControl = new ConsoleControl();
            mainGrid.Children.Add(outputControl);
            Grid.SetColumn(outputControl, 0);
            Grid.SetRow(outputControl, 2);
        }

        private void InitTurtleOutput()
        {
            outputControl = new TurtleControl();
            mainGrid.Children.Add(outputControl);
            Grid.SetColumn(outputControl, 0);
            Grid.SetRow(outputControl, 2);
        }

        #region Toolbox initialization

        /// <summary>
        /// Populates toolbox with elements that can be used in console project
        /// </summary>
        private void InitToolbox_Console()
        {
            InitToolbox_Common();
            ToolboxElementsList.Add(new ConsoleWriteElement());
            ToolboxElementsList.Add(new ConsoleClearElement());
        }

        /// <summary>
        /// Populates toolbox with elements that can be used in turtle graphics project
        /// </summary>
        private void InitToolbox_Turtle()
        {
            InitToolbox_Common();
            ToolboxElementsList.Add(new PenUpDownElement());
            ToolboxElementsList.Add(new TurtleGoElement());
            ToolboxElementsList.Add(new TurtleTurnElement());
            ToolboxElementsList.Add(new TurtleShowHideElement());
            ToolboxElementsList.Add(new TurtleClearElement());
        }

        /// <summary>
        /// Populates toolbox with elements that can be used in both console and turtle graphics project
        /// </summary>
        private void InitToolbox_Common()
        {
            ToolboxElementsList.Clear();
            if (Properties.Settings.Default.Settings_ExpertMode)
                ToolboxElementsList.Add(new RawScriptElement());
            ToolboxElementsList.Add(new SetVariableElement());
            ToolboxElementsList.Add(new ConditionIfElement());
            ToolboxElementsList.Add(new LoopElement());
        }

        #endregion Toolbox initialization

        #endregion Initialization methods

        #region Commands actions

        /// <summary>
        /// Creates a new console project and dynamically adds controls to the main grid
        /// </summary>
        public void CreateConsoleProject()
        {
            if (WorkspaceViewModel.Project != null)
                return;

            InitConsoleOutput();

            WorkspaceViewModel.Project = new ConsoleProject(((ConsoleControl)outputControl).GetOutputConsole());

            InitToolbox_Console();
        }

        /// <summary>
        /// Creates a new turtle graphics project and dynamically adds controls to the main grid
        /// </summary>
        public void CreateTurtleProject()
        {
            if (WorkspaceViewModel.Project != null)
                return;

            var settings = new TurtleProjectSettings() { Owner = owner };
            var result = settings.ShowDialog();

            if (!result.HasValue || !result.Value)
                return;

            var model = settings.DataContext as TurtleProjectSettingsModel;

            InitTurtleOutput();

            WorkspaceViewModel.Project = new TurtleProject(((TurtleControl)outputControl).GetTurtle(model))
            {
                TurtleProjectSettings = model
            };

            InitToolbox_Turtle();
        }

        public void OpenProject()
        {
            if (WorkspaceViewModel.Project != null)
                return;

            var dialog = new OpenFileDialog()
            {
                Filter = String.Format("{0} (*.gepl)|*.gepl|{1} (*.*)|*.*", Properties.Resources.FileFilter_Project, Properties.Resources.FileFilter_AllFiles)
            };

            var dialogResult = dialog.ShowDialog(owner);

            if (!dialogResult.Value)
                return;

            Project project = null;

            try
            {
                project = DataSerializer.DeserializeBinaryFromFile<Project>(dialog.FileName);
            }
            catch
            {
                MessageBox.Show(Properties.Resources.Error_DeserializationError, "", MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }

            if (project == null)
                return;

            var consoleProject = project as ConsoleProject;
            if (consoleProject != null)
            {
                InitConsoleOutput();
                InitToolbox_Console();
                consoleProject.Init(((ConsoleControl)outputControl).GetOutputConsole());
            }
            else
            {
                var turtleProject = project as TurtleProject;
                if (turtleProject == null)
                    return;
                InitTurtleOutput();
                InitToolbox_Turtle();
                turtleProject.Init(((TurtleControl)outputControl).GetTurtle(turtleProject.TurtleProjectSettings));
            }

            WorkspaceViewModel.Project = project;

            foreach (var element in WorkspaceViewModel.Project.Elements)
            {
                element.InitializeUserControlInstance();
            }
        }

        public void SaveAs()
        {
            if (WorkspaceViewModel.Project == null)
                return;

            var dialog = new SaveFileDialog()
            {
                Filter = String.Format("{0} (*.gepl)|*.gepl|{1} (*.*)|*.*", Properties.Resources.FileFilter_Project, Properties.Resources.FileFilter_AllFiles)
            };

            var dialogResult = dialog.ShowDialog(owner);

            if (!dialogResult.Value)
                return;

            DataSerializer.SerializeBinaryToFile(WorkspaceViewModel.Project, dialog.FileName);
        }

        public void SaveOutput()
        {
            if (WorkspaceViewModel.Project == null)
                return;

            var dialog = new SaveFileDialog();

            var consoleProject = WorkspaceViewModel.Project as ConsoleProject;
            var turtleProject = WorkspaceViewModel.Project as TurtleProject;
            if (consoleProject != null)
            {
                dialog.Filter = String.Format("{0} (*.txt)|*.txt|{1} (*.*)|*.*",
                                Properties.Resources.FileFIlter_Txt, Properties.Resources.FileFilter_AllFiles);

                var dialogResult = dialog.ShowDialog(owner);

                if (!dialogResult.Value)
                    return;

                using (var sw = new StreamWriter(dialog.FileName, false))
                {
                    sw.Write(consoleProject.Output.TextBox.Text);
                }
            }
            else if (turtleProject != null)
            {
                dialog.Filter = String.Format("{0} (*.bmp)|*.bmp|{1} (*.jpeg)|*.jpeg|{2} (*.png)|*.png",
                                Properties.Resources.FileFIlter_Bmp, Properties.Resources.FileFilter_Jpg, Properties.Resources.FileFilter_Png);

                var dialogResult = dialog.ShowDialog(owner);

                if (!dialogResult.Value)
                    return;

                var format = ImageFormat.Bmp;
                switch (dialog.FilterIndex)
                {
                    case 0:
                        format = ImageFormat.Bmp;
                        break;

                    case 1:
                        format = ImageFormat.Jpeg;
                        break;

                    case 2:
                        format = ImageFormat.Png;
                        break;
                }

                turtleProject.SavedImage.Save(dialog.FileName, format);
            }
        }

        public void CloseProject()
        {
            if (WorkspaceViewModel.Project == null)
                return;

            WorkspaceViewModel.Project = null;
            ToolboxElementsList.Clear();
            mainGrid.Children.Remove(outputControl);
        }

        /// <summary>
        /// Executes the program
        /// </summary>
        public void Execute()
        {
            if (WorkspaceViewModel.Project != null)
                WorkspaceViewModel.Project.Execute();
        }

        /// <summary>
        /// Displays a message box with code generated from current elements
        /// </summary>
        public void ShowCode()
        {
            var code = WorkspaceViewModel.Project.GetCodeFromElements();
            MessageBox.Show(owner, code);
        }

        /// <summary>
        /// Displays About dialog window
        /// </summary>
        public void ShowAbout()
        {
            var about = new AboutWindow() { Owner = owner };
            about.ShowDialog();
        }

        public void ExpertModeUpdate()
        {
            if (ToolboxElementsList.Count == 0)
                return;

            var rawScript = ToolboxElementsList.FirstOrDefault(i => i is RawScriptElement);
            if (Properties.Settings.Default.Settings_ExpertMode)
            {
                if (rawScript == null)
                    ToolboxElementsList.Insert(0, new RawScriptElement());
            }
            else
            {
                if (rawScript != null)
                    ToolboxElementsList.Remove(rawScript);
            }
        }

        public void ChangeLanguage()
        {
            if (AutoLangChecked)
                Properties.Settings.Default.Settings_Language = "auto";
            else if (EnLangChecked)
                Properties.Settings.Default.Settings_Language = "en";
            else if (PlLangChecked)
                Properties.Settings.Default.Settings_Language = "pl-PL";

            MessageBox.Show(owner, Properties.Resources.Message_LanguageChangeRestart, String.Empty, MessageBoxButton.OK,
                MessageBoxImage.Information);
        }

        public void ShowHelp()
        {
            if (helpWindow == null || !helpWindow.IsLoaded)
            {
                helpWindow = new HelpWindow();
                helpWindow.Owner = owner;
                helpWindow.Show();
            }
            else
            {
                helpWindow.Focus();
            }
        }

        #endregion Commands actions
    }
}